// Make sure the PCH goes first
//-----------------------------
#include "DXUT.h"
#include <atlbase.h>
// Include the D3D10 headers
//--------------------------
#include <d3dx9.h>
// Include any project-specific headers
//-------------------------------------
#include "..\\Utility\\UtilDefs.h"
#include "D3DX9MeshToD3D11Mesh.h"
#include "..\\Utility\\D3D\\D3DUtilityFunction.h"

/*HRESULT XMeshConv::CreateD3D9NullRef( IDirect3DDevice9** ppCreatedDevice )
{
		CComPtr< IDirect3D9 > pD3D9;
		pD3D9.Attach( Direct3DCreate9( D3D_SDK_VERSION ) );
		if( NULL == pD3D9 )
		{
			ERR_OUT( L"Failed to create IDirect3D9 interface" );

			return E_FAIL;
		}

		D3DDISPLAYMODE Mode;
		pD3D9->GetAdapterDisplayMode(0, &Mode);

		D3DPRESENT_PARAMETERS pp;
		ZeroMemory( &pp, sizeof(D3DPRESENT_PARAMETERS) ); 
		pp.BackBufferWidth  = 1;
		pp.BackBufferHeight = 1;
		pp.BackBufferFormat = Mode.Format;
		pp.BackBufferCount  = 1;
		pp.SwapEffect       = D3DSWAPEFFECT_COPY;
		pp.Windowed         = TRUE;

		if( FAILED( pD3D9->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_NULLREF, DXUTGetHWND(), D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, ppCreatedDevice ) ) )
		{
			ERR_OUT( L"Unable to create NULLREF device!" );

			return E_FAIL;
		}

		return S_OK;
	}*/



HRESULT XMeshConv::CreateD3D11MeshFromD3DX9Mesh( ID3DXMesh* pInMesh, ID3D11Device *pDevice, ID3D11Buffer** ppVB, ID3D11Buffer** ppIB, 
												 std::vector <MESH_ATTRIBUTE_RANGE> &Subsets)
	{
		// Check the parameters
		if( (NULL == pInMesh) || (NULL == pDevice) ) {
			ERR_OUT( L"One or more parameters are NULL when they shouldn't be!" );
			return E_INVALIDARG;
		}

		// Retrieve the declaration for the incoming data
		D3DVERTEXELEMENT9 OriginalDeclaration[ MAX_FVF_DECL_SIZE ];
		if( FAILED( pInMesh->GetDeclaration( OriginalDeclaration ) ) ) {
			ERR_OUT( L"Could not retrieve declaration from the D3DX9 mesh" );
			return E_FAIL;
		}

		// Convert the vertex declaration
		D3D11_INPUT_ELEMENT_DESC NewDeclaration[ MAX_FVF_DECL_SIZE ];
		ZeroMemory( NewDeclaration, sizeof( NewDeclaration ) );

		if( FAILED( ConvertDeclaration( OriginalDeclaration, NewDeclaration ) ) ) {
			ERR_OUT( L"Could not convert incoming D3D9 declaration to a D3D11 declaration" );
			return E_FAIL;
		}

		// Determine the number of elements in the declaration
		//UINT DeclSize = D3DXGetDeclLength( OriginalDeclaration );
		// Now we fill the D3D10 mesh with data
		if( FAILED( CreateVertexBufferData( pInMesh, ppVB, pDevice ) ) )
		{
			ERR_OUT( L"Unable to create the raw vertex data from D3X9 mesh to D3D11 mesh." );

			return E_FAIL;
		}

		if( FAILED( CreateIndexBufferData( pInMesh, ppIB, pDevice) ) )	{
			ERR_OUT( L"Unable to create the raw index data from D3DX9 mesh to D3D11 mesh." );
			return E_FAIL;
		}
		INFO_OUT( L"Successfully create all raw vertex and index data from D3DX9 mesh to D3D11 mesh." );

		// Set up the attribute table
		if( FAILED( ConfigureAttributeTable( pInMesh, Subsets) ) ) {
			ERR_OUT( L"Unable to configure attribute table for new mesh" );
			return E_FAIL;
		}		
		INFO_OUT( L"Successfully created a D3D11 mesh from a D3DX9 mesh." );
		return S_OK;
	}



	HRESULT XMeshConv::ConvertDeclaration( const D3DVERTEXELEMENT9 original[ MAX_FVF_DECL_SIZE ], D3D11_INPUT_ELEMENT_DESC output[ MAX_FVF_DECL_SIZE ] )
	{
		for( UINT i = 0; i < D3DXGetDeclLength( original ); ++i )
		{
			// Convert this element
			output[i].SemanticName			= ConvertSemantic( static_cast< D3DDECLUSAGE >( original[i].Usage ) );
			output[i].SemanticIndex			= original[i].UsageIndex;
			output[i].AlignedByteOffset		= original[i].Offset;
			output[i].InputSlot				= original[i].Stream;
			output[i].InputSlotClass		= D3D11_INPUT_PER_VERTEX_DATA;
			output[i].InstanceDataStepRate	= 0;
			output[i].Format				= ConvertType( static_cast< D3DDECLTYPE >( original[i].Type ) );
		}

		return S_OK;
	}



	LPCSTR XMeshConv::ConvertSemantic( const D3DDECLUSAGE& d3d9Form )
	{
		switch( d3d9Form )
		{
			case D3DDECLUSAGE_POSITION:				return "POSITION";
			case D3DDECLUSAGE_BLENDWEIGHT:			return "BLENDWEIGHT";
			case D3DDECLUSAGE_BLENDINDICES:			return "BLENDINDICES";
			case D3DDECLUSAGE_NORMAL:				return "NORMAL";
			case D3DDECLUSAGE_PSIZE:				return "PSIZE";
			case D3DDECLUSAGE_TEXCOORD:				return "TEXCOORD";
			case D3DDECLUSAGE_TANGENT:				return "TANGENT";
			case D3DDECLUSAGE_BINORMAL:				return "BINORMAL";
			case D3DDECLUSAGE_TESSFACTOR:			return "TESSFACTOR";
			case D3DDECLUSAGE_POSITIONT:			return "POSITIONT";
			case D3DDECLUSAGE_COLOR:				return "COLOR";
			case D3DDECLUSAGE_FOG:					return "FOG";
			case D3DDECLUSAGE_DEPTH:				return "DEPTH";
			case D3DDECLUSAGE_SAMPLE:				return "SAMPLE";
			default:								return "UNKNOWN";
		}
	}



	DXGI_FORMAT	XMeshConv::ConvertType( const D3DDECLTYPE& d3d9Form )
	{
		switch( d3d9Form )
		{
			case D3DDECLTYPE_FLOAT1:				return DXGI_FORMAT_R32_FLOAT;
			case D3DDECLTYPE_FLOAT2:				return DXGI_FORMAT_R32G32_FLOAT;
			case D3DDECLTYPE_FLOAT3:				return DXGI_FORMAT_R32G32B32_FLOAT;
			case D3DDECLTYPE_FLOAT4:				return DXGI_FORMAT_R32G32B32A32_FLOAT;
			case D3DDECLTYPE_D3DCOLOR:				return DXGI_FORMAT_R8G8B8A8_UNORM;
			case D3DDECLTYPE_UBYTE4:				return DXGI_FORMAT_R8G8B8A8_UINT;
			case D3DDECLTYPE_SHORT2:				return DXGI_FORMAT_R16G16_SINT;
			case D3DDECLTYPE_SHORT4:				return DXGI_FORMAT_R16G16B16A16_SINT;
			case D3DDECLTYPE_UBYTE4N:				return DXGI_FORMAT_R8G8B8A8_UNORM;
			case D3DDECLTYPE_SHORT2N:				return DXGI_FORMAT_R16G16_SNORM;
			case D3DDECLTYPE_SHORT4N:				return DXGI_FORMAT_R16G16B16A16_SNORM;
			case D3DDECLTYPE_USHORT2N:				return DXGI_FORMAT_R16G16_UNORM;
			case D3DDECLTYPE_USHORT4N:				return DXGI_FORMAT_R16G16B16A16_UNORM;
			case D3DDECLTYPE_UDEC3:					return DXGI_FORMAT_R10G10B10A2_UINT;
			case D3DDECLTYPE_DEC3N:					return DXGI_FORMAT_R10G10B10A2_UNORM;
			case D3DDECLTYPE_FLOAT16_2:				return DXGI_FORMAT_R16G16_FLOAT;
			case D3DDECLTYPE_FLOAT16_4:				return DXGI_FORMAT_R16G16B16A16_FLOAT;

			case D3DDECLTYPE_UNUSED:
			default:								return DXGI_FORMAT_UNKNOWN;
		}
	}


	//Default index value is 32bit
	HRESULT XMeshConv::CreateIndexBufferData( ID3DXMesh* pMesh9, ID3D11Buffer** ppIB, ID3D11Device* pd3dDevice)
	{
		// verify inputs
		if( (NULL == pMesh9) || (NULL == pd3dDevice) )
		{
			ERR_OUT( L"Both input mesh's must be non-NULL." );

			return E_INVALIDARG;
		}

		//clear IB before creation
		SAFE_RELEASE(*ppIB);
		// We now have direct access to the index buffer data
		// so we need to lock the incoming, D3DX9, buffer and copy the contents
		// across...
		DWORD options = pMesh9->GetOptions();
		if(options&D3DXMESH_32BIT)
		{
			unsigned int *pRawIndices9 = NULL;
			HRESULT hr = S_OK;
			if( SUCCEEDED( pMesh9->LockIndexBuffer( 0, reinterpret_cast< LPVOID* >( &pRawIndices9 ) ) ) )
			{
				printf("FaceNum:%d\n", pMesh9->GetNumFaces());
				V_RETURN(CD3DUtilityFunction::CreateIndexBuffer(pd3dDevice,pMesh9->GetNumFaces()*3,(void*)(pRawIndices9),ppIB));
				// Tidy up
				pMesh9->UnlockIndexBuffer( );
			}
			else
			{
				ERR_OUT( L"Unable to lock the D3DX9 mesh's index buffer!" );			
				return E_FAIL;
			}

		}
		else
		{
			unsigned short *pRawIndices9 = NULL;
			HRESULT hr = S_OK;
			if( SUCCEEDED( pMesh9->LockIndexBuffer( 0, reinterpret_cast< LPVOID* >( &pRawIndices9 ) ) ) )
			{
				printf("FaceNum:%d\n", pMesh9->GetNumFaces());
				V_RETURN(CD3DUtilityFunction::CreateIndexBuffer(pd3dDevice,pMesh9->GetNumFaces()*3,(void*)(pRawIndices9),ppIB, false));
				// Tidy up
				pMesh9->UnlockIndexBuffer( );
			}
			else
			{
				ERR_OUT( L"Unable to lock the D3DX9 mesh's index buffer!" );			
				return E_FAIL;
			}

		}
				return S_OK;
	}

	HRESULT XMeshConv::CreateVertexBufferData( ID3DXMesh* pMesh9, ID3D11Buffer** ppVB, ID3D11Device* pd3dDevice )
	{
		if( (NULL == pMesh9) )
		{
			ERR_OUT( L"Incoming meshes must be valid pointers." );
			return E_INVALIDARG;
		}
		//clear VB before creation
		SAFE_RELEASE(*ppVB);
		// Now we need to acquire the input lock
		HRESULT hr = S_OK;
		float* pRawVertices9 = NULL;
		if( SUCCEEDED( pMesh9->LockVertexBuffer( 0, reinterpret_cast< LPVOID* >( &pRawVertices9 ) ) ) )
		{
			//create d3d11 vb
			printf("VertNum:%d, Stride:%d\n",pMesh9->GetNumVertices(),pMesh9->GetNumBytesPerVertex());
			V_RETURN(CD3DUtilityFunction::CreateVertexBuffer(pd3dDevice,pMesh9->GetNumBytesPerVertex(),pRawVertices9, 
				pMesh9->GetNumVertices(), ppVB));
			pMesh9->UnlockVertexBuffer( );
		}
		else
		{
			printf( "Failed to lock D3DX9 mesh's vertex buffer!\n" );
#ifdef _DEBUG
			OUTPUTERRORINFO;
#endif
			return E_FAIL;
		}
		return S_OK;
	}
	
	HRESULT XMeshConv::ConfigureAttributeTable( ID3DXMesh* pMesh9, std::vector <MESH_ATTRIBUTE_RANGE> &Subsets)
	{
		// verify inputs
		if( (NULL == pMesh9) ) {
			ERR_OUT( L"Both input mesh's must be non-NULL." );
			return E_INVALIDARG;
		}

		DWORD dwInputTableSize = 0;
		if( FAILED( pMesh9->GetAttributeTable( NULL, &dwInputTableSize ) ) ) {
			ERR_OUT( L"Unable to retrieve the size of the incoming mesh's attribute table" );
			return E_FAIL;
		}

		//clear materials and subsets before filling
		Subsets.clear();
		DWORD *attribBuf;
		HRESULT hr;
		
		if( 0 == dwInputTableSize )	{
			// The incoming mesh doesn't have an attribute table
			INFO_OUT( L"Incoming D3DX9 mesh doesn't have an attribute table, creating a default table." );

			MESH_ATTRIBUTE_RANGE defTable;

			defTable.AttribId		= 0;
			defTable.FaceCount		= pMesh9->GetNumFaces( );
			defTable.FaceStart		= 0;
			defTable.VertexCount	= pMesh9->GetNumVertices();
			defTable.VertexStart	= 0;
			defTable.MatIndex       = 0;
			Subsets.push_back(defTable);			
		}
		else
		{
			D3DXATTRIBUTERANGE *pInputAttrTable = new D3DXATTRIBUTERANGE[ dwInputTableSize ];
			if( FAILED( pMesh9->GetAttributeTable( pInputAttrTable, &dwInputTableSize ) ) )	{	
				ERR_OUT( L"Unable to retrieve the incoming mesh's attribute table" );
				SAFE_DELETE_ARRAY( pInputAttrTable );
				return E_FAIL;
			}
			//find the corresponding material index for current subset!
			if (SUCCEEDED(hr=pMesh9->LockAttributeBuffer(D3DLOCK_DISCARD,&attribBuf))) {
				for( DWORD i = 0; i < dwInputTableSize; ++i ) {
					MESH_ATTRIBUTE_RANGE range;
					range.AttribId		= pInputAttrTable[i].AttribId;
					range.FaceCount		= pInputAttrTable[i].FaceCount;
					range.FaceStart		= pInputAttrTable[i].FaceStart;
					range.VertexCount	= pInputAttrTable[i].VertexCount;
					range.VertexStart	= pInputAttrTable[i].VertexStart;
					range.MatIndex      = *(attribBuf+range.FaceStart);
					Subsets.push_back(range);
				}
				SAFE_DELETE_ARRAY( pInputAttrTable );			
			}
			// unlock the attribute buffer
			pMesh9->UnlockAttributeBuffer();
		}

		return S_OK;
	}
